package com.wl.common.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.wl.common.core.web.dto.DataStoreDTO;
import com.wl.common.mybatis.utils.PageUtils;
import com.wl.common.system.dao.SysResourceCategoryMapper;
import com.wl.common.system.dao.SysResourceMapper;
import com.wl.common.system.dao.SysRoleResourceMapper;
import com.wl.common.system.domain.SysResource;
import com.wl.common.system.domain.SysResourceCategory;
import com.wl.common.system.domain.SysRoleResource;
import com.wl.common.system.service.SysResourceCategoryService;
import com.wl.common.system.support.dto.SysResourceCategoryDTO;
import com.wl.common.system.support.dto.query.SysResourceCategoryQueryDTO;
import com.wl.common.system.support.vo.SysResourceCategoryVO;
import com.wl.common.system.support.vo.SysResourceVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 后台资源类型表 服务实现类
 *
 * @author wanglin
 * @since 2023-09-18
 */
@Service
public class SysResourceCategoryServiceImpl implements SysResourceCategoryService {

    @Autowired
    private SysResourceCategoryMapper sysResourceCategoryMapper;
    @Autowired
    private SysResourceMapper resourceMapper;
    @Autowired
    private SysRoleResourceMapper roleResourceMapper;

    @Transactional(readOnly = true)
    @Override
    public DataStoreDTO<SysResourceCategoryVO> page(Pageable pageable, SysResourceCategoryQueryDTO queryDto) {
        QueryWrapper<SysResourceCategory> queryWrapper = this.buildQuery(queryDto);
        Page<SysResourceCategory> page = PageUtils.transferPage(pageable);
        Page<SysResourceCategory> result = this.sysResourceCategoryMapper.selectPage(page, queryWrapper);
        return new DataStoreDTO(result.getTotal(), this.transferVo(result.getRecords()));
    }

    @Transactional(readOnly = true)
    @Override
    public List<SysResourceCategoryVO> list(Sort sort, SysResourceCategoryQueryDTO queryDto) {
        QueryWrapper<SysResourceCategory> queryWrapper = this.buildQuery(queryDto);
        PageUtils.transferSort(queryWrapper, sort);
        return this.transferVo(this.sysResourceCategoryMapper.selectList(queryWrapper));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(SysResourceCategoryDTO dto) {
        dto.setId(null);
        //唯一性验证 name
        Assert.isNull(checkByName(dto.getName()), "资源分类名称: " + dto.getName() + "已存在");
        sysResourceCategoryMapper.insert(this.transferEntity(null, dto));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(SysResourceCategoryDTO dto) {
        Assert.hasText(dto.getId(), "id不能为空");
        SysResourceCategory entity = sysResourceCategoryMapper.selectById(dto.getId());
        Assert.notNull(entity, "找不到id为 " + dto.getId() + " 的记录");

        SysResourceCategory category = checkByName(dto.getName());
        if (ObjUtil.isNotNull(category)) {
            Assert.isTrue(dto.getId().equals(category.getId()), "资源分类名称: " + dto.getName() + "已存在");
        }
        sysResourceCategoryMapper.updateById(this.transferEntity(entity, dto));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Set<String> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return;
        }
        //资源分类下是否包含资源
        List<SysResource> resourceList = resourceMapper.selectList(new LambdaQueryWrapper<SysResource>().in(SysResource::getCategoryId, ids));
        //若不为空，则验证资源是否绑定角色
        if (CollUtil.isNotEmpty(resourceList)) {
            List<String> resourceIdList = resourceList.stream().map(e -> e.getId()).collect(Collectors.toList());
            List<SysRoleResource> roleResourceList = roleResourceMapper.selectList(new LambdaQueryWrapper<SysRoleResource>().in(SysRoleResource::getResourceId, resourceIdList));
            Assert.isTrue(CollUtil.isEmpty(roleResourceList), "删除的资源分类已绑定角色，请先删除绑定关系！");
            //删除资源表数据
            resourceMapper.deleteBatchIds(resourceIdList);
        }
        sysResourceCategoryMapper.deleteBatchIds(ids);
    }

    @Transactional(readOnly = true)
    @Override
    public SysResourceCategoryVO get(String id) {
        Assert.hasText(id, "id不能为空");
        SysResourceCategory entity = sysResourceCategoryMapper.selectById(id);
        Assert.notNull(entity, "找不到id为 " + id + " 的记录");
        return this.transferVo(entity);
    }

    @Transactional(readOnly = true)
    @Override
    public List<SysResourceCategoryVO> treeResourceList(SysResourceCategoryQueryDTO queryDto) {
        List<SysResourceCategoryVO> voList = sysResourceCategoryMapper.selectResourceCategoryList(this.buildQuery(queryDto));
        if (CollUtil.isNotEmpty(voList)) {
            QueryWrapper<SysResource> queryWrapper = this.buildBatchQuery(queryDto);
            List<SysResourceVO> resourceList = resourceMapper.selectResourceList(queryWrapper);
            voList.stream().forEach(e -> e.setChildren(resourceList.stream().filter(x -> x.getCategoryId().equals(e.getId())).collect(Collectors.toList())));
        }
        return voList;
    }

    @Transactional(readOnly = true)
    @Override
    public DataStoreDTO<SysResourceCategoryVO> treeResourcePage(Pageable pageable, SysResourceCategoryQueryDTO queryDto) {
        IPage<SysResourceCategory> page = PageUtils.transferPage(pageable);
        IPage<SysResourceCategoryVO> result = sysResourceCategoryMapper.selectResourceCategoryPage(page, this.buildQuery(queryDto));
        if (CollUtil.isNotEmpty(result.getRecords())) {
            QueryWrapper<SysResource> queryWrapper = this.buildBatchQuery(queryDto);
            List<SysResourceVO> resourceList = resourceMapper.selectResourceList(queryWrapper);
            result.getRecords().stream().forEach(e -> e.setChildren(resourceList.stream().filter(x -> x.getCategoryId().equals(e.getId())).collect(Collectors.toList())));
        }
        return new DataStoreDTO(result.getTotal(), result.getRecords());
    }

    private SysResourceCategory checkByName(String name) {
        if (StringUtils.isBlank(name)) {
            return null;
        }
        return sysResourceCategoryMapper.selectOne(new LambdaQueryWrapper<SysResourceCategory>().eq(SysResourceCategory::getName, name));
    }

    private QueryWrapper<SysResourceCategory> buildQuery(SysResourceCategoryQueryDTO queryDto) {
        QueryWrapper<SysResourceCategory> queryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(queryDto)) {
            queryWrapper.lambda()
                    .eq(SysResourceCategory::getDeleted, NumberUtils.INTEGER_ZERO)
                    .like(StringUtils.isNotBlank(queryDto.getName()), SysResourceCategory::getName, queryDto.getName())
                    .ge(StringUtils.isNotBlank(queryDto.getStartCreateTime()), SysResourceCategory::getCreateTime, queryDto.getStartCreateTime())
                    .le(StringUtils.isNotBlank(queryDto.getEndCreateTime()), SysResourceCategory::getCreateTime, queryDto.getEndCreateTime());
        }
        return queryWrapper;
    }

    private QueryWrapper<SysResource> buildBatchQuery(SysResourceCategoryQueryDTO queryDto) {
        QueryWrapper<SysResource> queryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(queryDto)) {
            queryWrapper
                    .eq("r.deleted", 0)
                    .eq("rc.deleted", 0)
                    .eq(StringUtils.isNotBlank(queryDto.getId()), "rc.id", queryDto.getId())
                    .like(StringUtils.isNotBlank(queryDto.getName()), "rc.name", queryDto.getName());
        }
        return queryWrapper;
    }

    private SysResourceCategory transferEntity(SysResourceCategory entity, SysResourceCategoryDTO dto) {
        if (Objects.isNull(entity)) {
            entity = new SysResourceCategory();
        }
        BeanUtils.copyProperties(dto, entity);
        return entity;
    }

    private List<SysResourceCategoryVO> transferVo(List<SysResourceCategory> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            return Lists.newArrayList();
        }

        List<SysResourceCategoryVO> voList = entities.stream().map(entity -> {
            SysResourceCategoryVO vo = new SysResourceCategoryVO();
            BeanUtils.copyProperties(entity, vo);
            return vo;
        }).collect(Collectors.toList());
        return voList;
    }

    private SysResourceCategoryVO transferVo(SysResourceCategory entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        SysResourceCategoryVO vo = new SysResourceCategoryVO();
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }
}
